﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace WindowsFormTest.Communication
{
    

    public class MitsubishiFX5U
    {
        private TcpClient _tcpClient;
        private NetworkStream _networkStream;
        private string _ipAddress;
        private int _port;
        private readonly int _timeout = 2000; // 2秒超时
        private readonly object _lock = new object();

        public bool IsConnected => _tcpClient?.Connected == true;

        public void Connect(string ipAddress = "192.168.0.10", int port = 5002)
        {
            lock (_lock)
            {
                if (IsConnected) return;

                _ipAddress = ipAddress;
                _port = port;

                Disconnect();

                _tcpClient = new TcpClient();
                var result = _tcpClient.BeginConnect(ipAddress, port, null, null);
                var success = result.AsyncWaitHandle.WaitOne(_timeout);

                if (!success || !_tcpClient.Connected)
                {
                    Disconnect();
                    throw new Exception("PLC连接失败");
                }

                _tcpClient.EndConnect(result);
                _networkStream = _tcpClient.GetStream();
            }
        }

        public void Reconnect()
        {
            Disconnect();
            Connect(_ipAddress, _port);
        }

        public void Disconnect()
        {
            lock (_lock)
            {
                if (_networkStream != null)
                {
                    _networkStream.Close();
                    _networkStream = null;
                }

                if (_tcpClient != null)
                {
                    _tcpClient.Close();
                    _tcpClient = null;
                }
            }
        }

        public ushort[] ReadD(int startAddress, int length)
        {
            ValidateReadParams(startAddress, length, 0, 6144); // D0~D6144

            // 构建读字命令帧 (D区)
            var command = BuildReadCommand(
                isBit: false,
                startAddress: (uint)startAddress * 2, // D区地址需要×2
                length: (ushort)length
            );

            var response = SendCommand(command);
            return ParseWordResponse(response, length);
        }

        public bool[] ReadM(int startAddress, int length)
        {
            ValidateReadParams(startAddress, length, 0, 7679); // M0~M7679

            // 构建读位命令帧 (M区)
            var command = BuildReadCommand(
                isBit: true,
                startAddress: (uint)startAddress, // M区直接使用位地址
                length: (ushort)length
            );

            var response = SendCommand(command);
            return ParseBitResponse(response, length);
        }

        private void ValidateReadParams(int startAddress, int length, int minAddress, int maxAddress)
        {
            if (startAddress < minAddress || startAddress > maxAddress)
                throw new ArgumentException($"地址范围错误：{minAddress}~{maxAddress}");

            if (length < 1 || (startAddress + length) > maxAddress)
                throw new ArgumentException($"读取长度错误：1~{maxAddress - startAddress}");
        }

        private byte[] BuildReadCommand(bool isBit, uint startAddress, ushort length)
        {
            var frame = new List<byte>();

            // 帧头
            frame.AddRange(new byte[] { 0x50, 0x00 }); // 副头部
            frame.Add(0x00); // 网络编号
            frame.Add(0xFF); // PLC编号
            frame.AddRange(new byte[] { 0xFF, 0x00 }); // 目标模块IO号
            frame.Add(0x00); // 目标站号

            // 构建命令数据
            var commandData = new List<byte>();
            commandData.Add(0x01); // 读取命令
            commandData.Add(0x04); // 子命令
            commandData.AddRange(isBit ? new byte[] { 0x01, 0x00 } : new byte[] { 0x00, 0x00 }); // 位/字标识

            // 地址处理（小端序）
            commandData.AddRange(BitConverter.GetBytes(startAddress).Take(4));
            // 长度处理（小端序）
            commandData.AddRange(BitConverter.GetBytes(length).Take(2));

            // 设置数据长度（小端序）
            ushort dataLength = (ushort)commandData.Count;
            frame.AddRange(BitConverter.GetBytes(dataLength));
            frame.AddRange(commandData);

            return frame.ToArray();
        }

        private byte[] SendCommand(byte[] command)
        {
            lock (_lock)
            {
                if (!IsConnected) throw new Exception("PLC未连接");

                // 发送命令
                _networkStream.Write(command, 0, command.Length);

                // 读取响应头（11字节）
                var header = new byte[11];
                ReadBytes(header, 0, 11);

                // 获取响应数据长度（小端序）
                int dataLength = BitConverter.ToUInt16(new byte[] { header[9], header[10] }, 0);

                // 读取响应数据
                var response = new byte[dataLength];
                ReadBytes(response, 0, dataLength);

                // 检查错误码
                if (dataLength < 2 || response[0] != 0 || response[1] != 0)
                    throw new Exception($"PLC返回错误: 0x{response[0]:X2}{response[1]:X2}");

                return response.Skip(2).ToArray(); // 跳过错误码
            }
        }

        private void ReadBytes(byte[] buffer, int offset, int count)
        {
            int read = 0;
            while (read < count)
            {
                var remaining = count - read;
                var received = _networkStream.Read(buffer, offset + read, remaining);
                if (received == 0) throw new Exception("网络流中断");
                read += received;
            }
        }

        private ushort[] ParseWordResponse(byte[] data, int expectedLength)
        {
            if (data.Length < expectedLength * 2)
                throw new Exception("响应数据长度不足");

            var result = new ushort[expectedLength];
            for (int i = 0; i < expectedLength; i++)
            {
                // PLC返回的字数据是小端序
                result[i] = BitConverter.ToUInt16(new byte[] { data[i * 2], data[i * 2 + 1] }, 0);
            }
            return result;
        }

        private bool[] ParseBitResponse(byte[] data, int expectedLength)
        {
            if (data.Length < (expectedLength + 7) / 8)
                throw new Exception("响应数据长度不足");

            var result = new bool[expectedLength];
            for (int i = 0; i < expectedLength; i++)
            {
                int byteIndex = i / 8;
                int bitIndex = i % 8;
                result[i] = (data[byteIndex] & (1 << bitIndex)) != 0;
            }
            return result;
        }
    }
}
